#ifndef EVALUATE_H
#define EVALUATE_H

#define NDEBUG
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include <cmath>
#include <climits>
#include <algorithm>
#include <iomanip>
#include <queue>
#include <cassert>
#include <utility>
#include <set>
#include <cstring>

#define BIN_AREA_THRESHOLD   0.2
#define FREE_SPACE_THRESHOLD 0.2
#define BIN_DIM              9
#define ALPHA                1.0
// #define VERTICAL 			 0.95
// #define HORIZONTAL			 0.9	

#define ABU2_WGT 10
#define ABU5_WGT  4
#define ABU10_WGT 2
#define ABU20_WGT 1

using namespace std;

/*****************************************/
/*		Struct Declarations     		 */	
/*****************************************/

struct pin
{
	double x_offset;
	double y_offset;
	unsigned pin2net;
};

struct node
{
	int x_coord; 
	int y_coord;
	int initial_x;
	int initial_y; 
	int width;   
	int height;
	int ID;
	string obj_name;
	bool isFixed;                /* fixed cell or not */
	bool isNI;                   /* is termianl NI or not */
	vector<unsigned> shapeIds;   /* for non-rectangular shaped fixed cells */
	vector<pin> pins;
};

struct net
{
	vector<unsigned> nodeIds;
	vector<double> x_offset;
	vector<double> y_offset;
	double max_X;
	double min_X;
	double max_Y;
	double min_Y;
	string net_name;
};

struct row
{
	int coordinate;
	int height;
	int sitewidth;
	int sitespacing;
	string siteorient;
	string sitesymmetry;
	int subrowOrigin;
	unsigned numSites;
	int rx;                     /* row's right end location */
};

struct tinyRow 
{
	int ID;
	int lx; 
	int rx;
	int y;
	int l_end;
	int r_end;
	bool isValid;
	vector<int> NodeID;
};

struct ckt
{
	string benchmark;              /* benchmark name */
	map<string, unsigned> name2id; /* map between cell name and ID */
	double total_mArea;            /* total movable cell area */
	double total_fArea;            /* total fixed cell area (excluding terminal NIs) */
	double designArea;             /* total placeable area (excluding row blockages) */
	unsigned numRows;
	int rowHeight;
	int lx, rx, by, ty;         /* placement image's left/right/bottom/top end coordintes */
	vector<tinyRow> tinyRows;      //Row Segments in a bin
	vector<node> nodes;            /* node list */
	vector<node> blks;             /* row blockage list */
	vector<node> shapes;           /* shape list */
	vector<net> nets;           
	vector<row> rows;
	//vector<bin> bins;
};

struct density_bin 
{
	int lx, hx;              /* low/high x coordinate */
	int ly, hy;              /* low/high y coordinate */
	int area;                /* bin area */
	double m_util;              /* bin's movable cell area */
	double f_util;              /* bin's fixed cell area */
	double free_space;          /* bin's freespace area */
};

struct query
{
	int cellID_left;
	int cellID_right;
	int cellID_second_left;
	int cellID_second_right;
	int space; // The space between two adjacent cells
	int space_left;
	int space_right;
	int available_space;
};

struct Candidate
{
	node * cur_node;
	tinyRow * src_row;
	queue<tinyRow*> dest_row; 
};

struct compareNodeID
{
	bool operator()(int a, int b);
};

/***********************************************************/
//		Manipulations (large displacement limits)     
/***********************************************************/
void global_move(ckt & ptrckt, int binID, double T1, double T2);
int insert2tinyRow_global(ckt & ptrckt, node & this_node, tinyRow & dest_row, int threshold);
bool calculate_candidate_global_move(ckt & ptrckt, Candidate & temp, double T2);
void bin_manipulation_1(ckt & ptrckt);
void bin_manipulation_2(ckt & ptrckt);
void intra_bin_optimization_1(ckt & ptrckt, int binID);
void intra_bin_optimization_2(ckt & ptrckt, int binID);
bool row_optimization(ckt & ptrckt, tinyRow & this_row);
int tri_swap_optimize(ckt & ptrckt, tinyRow & this_row, int index);
bool row_swap(ckt & ptrckt, tinyRow & this_row);
bool vertical_optimization(ckt & ptrckt, tinyRow & this_row, tinyRow & dest_row);
// Local ones
int tri_swap(ckt & ptrckt, tinyRow & this_row, int index);
int tri_swap(ckt & ptrckt, tinyRow & this_row, node & node1, node & node2, node & node3);
int bi_swap(ckt & ptrckt, tinyRow & this_row, int index);
int bi_swap(ckt & ptrckt, tinyRow & this_row, node & node1, node & node2);

/***********************************************************/
//		Reduce density (large displacement limit)     
/***********************************************************/
void reduce_density_1(ckt & ptrckt, double T1, double T2, int threshold);
bool calculate_candidate_1(ckt & ptrckt, Candidate & temp, double T2);
void reduce_density_2(ckt & ptrckt, double T1, double T2, int threshold);
bool calculate_candidate_2(ckt & ptrckt, Candidate & temp, double T2);
void reduce_density_3(ckt & ptrckt, double T1, double T2, int threshold);
bool calculate_candidate_3(ckt & ptrckt, Candidate & temp, double T2);

/***********************************************************/
//		Manipulations (small displacement limits)     
/***********************************************************/
void bin_manipulation_check(ckt & ptrckt);
void bin_optimization_check(ckt & ptrckt, int binID);
bool row_swap_optimization_check(ckt & ptrckt, tinyRow & this_row);
int insert2tinyRow_check(ckt & ptrckt, node & this_node, tinyRow & dest_row);
int insert2tinyRow_check(ckt & ptrckt, node & this_node, tinyRow & dest_row, int threshold);
pair<int,int> find_best_row_check(ckt & ptrckt, int src_row, int binID, node & this_node);
int tri_swap_check(ckt & ptrckt, tinyRow & this_row, int index);
int bi_swap_check(ckt & ptrckt, tinyRow & this_row, int index);
bool calculate_candidate_check(ckt & ptrckt, Candidate & temp, double T2);
void addBin2Queue(ckt & ptrckt, int curBinID, Candidate & temp, tinyRow & this_row);
bool move_node_in_row_check(ckt & ptrckt, node & this_node, int left_bound, int right_bound, int step);
/***********************************************************/
//		Reduce density (small displacement limit)  
// 		note: ensure displacement limit satisfied 
/***********************************************************/
void reduce_density_check(ckt & ptrckt, double T1, double T2, int T3);
/***********************************************************/
//		Utility Functions (large displacement limit)
/***********************************************************/
bool space_query(ckt & ptrckt, node & this_node, tinyRow & this_row, int x, query & answer);
bool insert2site(ckt & ptrckt, node & this_node, tinyRow & this_row, int x);
int insert2tinyRow(ckt & ptrckt, node & this_node, tinyRow & dest_row, int threshold);
pair<int,int> find_best_row(ckt & ptrckt, int src_row, int binID, node & this_node);
double cost(ckt & ptrckt, tinyRow & this_row, int i, int x);
double placement_cost(ckt & ptrckt, tinyRow & this_row, int i, int x);
void place_tinyRow_DP(ckt & ptrckt, tinyRow & this_row);

/***********************************************************/
//		Utility Functions (small displacement limit)  
// 		note: ensure displacement limit satisfied 
/***********************************************************/
//bool space_query_check(ckt & ptrckt, node & this_node, tinyRow & this_row, int x, query & answer);
bool insert2site_check(ckt & ptrckt, node & this_node, tinyRow & this_row, int x);
int insert2tinyRow_check(ckt & ptrckt, node & this_node, tinyRow & this_row);
void fix_displacement(ckt & ptrckt);
/***********************************************************/
//		General Utility Functions  
/***********************************************************/
void fixNet(ckt & ptrckt, node & this_node);
void fixRow(ckt & ptrckt, tinyRow & this_row);
void tinyRow_setup(ckt & ptrckt);
void util_array_setup(ckt & ptrckt, vector<double> & util_array);
int index_translate(int binId, int rowIndex);
int rowid2binID(int rowID);
int xy2tinyRowID(ckt & ptrckt, int x, int y);
void erase_src_node(ckt & ptrckt, tinyRow & src_row, int ID);
bool move_node_in_row(ckt & ptrckt, node & node2move, int left_bound, int right_bound);
bool move_node_in_row(ckt & ptrckt, node & this_node, int left_bound, int right_bound, int step);
void initialize_net(ckt & ptrckt);
int opt_center(ckt & ptrckt, node & this_node);
/***********************************************************/
//		Evaluation Functions  
/***********************************************************/
int read_bookshelf(ckt & ptrckt, int argc, char** argv);
double measure_HPWL(ckt & ptrckt);
double measure_ABU(ckt & ptrckt, double unit, double targUt);
double measure_displacement(ckt & ptrckt, const char* input);
void output_placement(ckt & ptrckt, const char* filename);
int delta_HPWL(ckt & ptrckt, int NodeID, int x_displacement, int y_displacement);
int cell_displacement(int target_x, int target_y, node & this_node);
int max_distance(ckt & ptrckt, int dest_row_ID, node & this_node);
int minimum_distance_node2row(ckt & ptrckt, node & this_node, tinyRow & dest_row);
double calculate_local_HPWL(ckt & ptrckt, set<int> & related_nets);
double single_net_HPWL(ckt & ptrckt, net & this_net);
int delta_HPWL_abs(ckt & ptrckt, node & this_node, int dest_x, int dest_y);
int tinyrow_distance(tinyRow & src_row, tinyRow & dest_row);
void tinyRow_statistics(ckt & ptrckt);
/***********************************************************/
//		Global Variables  
/***********************************************************/
extern int gridUnit;
extern int x_gridNum;
extern int y_gridNum;
extern int numBins;
extern ckt this_ckt;
extern string directory;
extern vector<double> util_array;


#endif